package com.ambimmort.prserver.repository;

import com.ambimmort.app.framework.uitls.Application;
import com.ambimmort.prserver.bean.MessageNoPoolBean;
import com.ambimmort.prserver.bean.SVNFileBean;
import com.ambimmort.prserver.bean.RepositoryOperationLogBean;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcPooledConnectionSource;
import com.j256.ormlite.table.TableUtils;
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.h2.store.fs.FileUtils;

/**
 *
 * @author 定巍
 */
public class Repository {

    private long createTime;
    private RepositoryEntry entry;
    private static String dir = Application.getConfDir().getAbsolutePath()+"/repo";
    private JdbcPooledConnectionSource connectionSource = null;
    private Dao<MessageNoPoolBean, Long> messageNoDao = null;
    private Dao<SVNFileBean, Long> svnFileDao = null;
    private Dao<RepositoryOperationLogBean, Long> svnLogDao = null;
    private RepositoryKit svnKit = null;

    private static Map<RepositoryEntry, Repository> instances = new HashMap<RepositoryEntry, Repository>();

    
    public static void deleteInstance(String baseDir,String type, String instanceName){
         RepositoryEntry re = repositoryEntry(baseDir, type, instanceName);
         Repository r = instances.get(re);
        try {

            if((r!=null)&&!r.isClosed()){
                r.close();
            }
            FileUtils.deleteRecursive(re.getBaseDir()+"/"+type+"/"+instanceName, true);
        } catch (SQLException ex) {
            java.util.logging.Logger.getLogger(Repository.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
    }

    public static boolean hasInstance(RepositoryEntry entry) {
        return instances.get(entry)!=null;
    }
    
    public static Repository getInstance(RepositoryEntry entry, boolean initialize) {
        if (instances.containsKey(entry)) {
            return instances.get(entry);
        } else {
            Repository repo = new Repository(entry, initialize);
            instances.put(entry, repo);

            return repo;
        }
    }

    public static String getDir() {
        return dir;
    }

    public static List<Repository> getAllRepositorys() {
        List<Repository> list = new ArrayList<Repository>();
        for (Repository i : instances.values()) {
            list.add(i);
        }
        return list;
    }

    public static void init() {
        try {
            File baseDirFile = new File(dir);
            if (!baseDirFile.exists()) {
                baseDirFile.mkdirs();
            }
            for (File typeDir : baseDirFile.listFiles()) {
                String typeName = typeDir.getName();
                if (typeDir.isDirectory()) {
                    for (File instanceFile : typeDir.listFiles()) {
                        Repository.getInstance(Repository.repositoryEntry(dir, typeName, instanceFile.getName()), false);
                    }
                }
            }
        } catch (Throwable t) {
            System.exit(-1);
        }

    }

    public Repository(RepositoryEntry entry, boolean initialize) {
        this.entry = entry;
        this.createTime = System.currentTimeMillis();

        File baseDirFile = new File(entry.baseDir);
        baseDirFile.mkdirs();
        File repoTypeDirFile = new File(baseDirFile, entry.getMessageType());
        repoTypeDirFile.mkdirs();
        File repoDirFile = new File(repoTypeDirFile, entry.instanceName);
        repoDirFile.mkdirs();
        if (initialize) {
            for (File f : repoDirFile.listFiles()) {

                FileUtils.delete(f.getAbsolutePath());
            }
        }

        try {
            connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + repoDirFile.getAbsolutePath() + "/" + entry.instanceName);
            connectionSource.setMaxConnectionAgeMillis(5 * 60 * 1000);
            // change the check-every milliseconds from 30 seconds to 60
            connectionSource.setCheckConnectionsEveryMillis(60 * 1000);
            connectionSource.setTestBeforeGet(true);
            initSVNMessageNoPool(initialize);
            initSVNFile(initialize);
            initSVNLog(initialize);
            svnKit = new RepositoryKit(this);

        } catch (SQLException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.ERROR, null, ex);
        }
    }

    public Dao<MessageNoPoolBean, Long> getMessageNoDao() {
        return messageNoDao;
    }

    public Dao<SVNFileBean, Long> getSvnFileDao() {
        return svnFileDao;
    }

    public Dao<RepositoryOperationLogBean, Long> getSvnLogDao() {
        return svnLogDao;
    }

    public RepositoryKit getRepositoryKit() {
        return svnKit;
    }

    private void initSVNMessageNoPool(boolean initialize) {
        try {
            TableUtils.createTableIfNotExists(connectionSource, MessageNoPoolBean.class);
            messageNoDao = DaoManager.createDao(connectionSource, MessageNoPoolBean.class);
            if (initialize) {
                messageNoDao.deleteBuilder().delete();
            }

        } catch (Exception ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.ERROR, null, ex);
        } finally {

        }
    }

    private void initSVNLog(boolean initialize) {
        try {
            TableUtils.createTableIfNotExists(connectionSource, RepositoryOperationLogBean.class);
            svnLogDao = DaoManager.createDao(connectionSource, RepositoryOperationLogBean.class);
            if (initialize) {
                svnLogDao.deleteBuilder().delete();
            }
        } catch (Exception ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.ERROR, null, ex);
        } finally {
        }
    }

    private void initSVNFile(boolean initialize) {
        try {
            TableUtils.createTableIfNotExists(connectionSource, SVNFileBean.class);
            svnFileDao = DaoManager.createDao(connectionSource, SVNFileBean.class);
            if (initialize) {
                svnFileDao.deleteBuilder().delete();
            }
        } catch (Exception ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.ERROR, null, ex);
        } finally {
        }
    }

    public void close() throws SQLException {
        instances.remove(this.entry);
        if (connectionSource.isOpen()) {
            connectionSource.close();
        }
    }

    public boolean isClosed() {
        return !connectionSource.isOpen() && (!instances.containsKey(this.entry));
    }

    public static RepositoryEntry repositoryEntry(String baseDir, String messageType, String instanceName) {
        return new RepositoryEntry(baseDir, instanceName, messageType);
    }

    public static class RepositoryEntry {

        private String instanceName;
        private String messageType;
        private String baseDir = Application.getConfDir().getAbsolutePath()+"/repo";

        public RepositoryEntry(String baseDir, String instanceName, String messageType) {
            this.instanceName = instanceName;
            this.messageType = messageType;
            this.baseDir = Application.getDataDir().getAbsolutePath()+"/"+baseDir;
        }

        public RepositoryEntry(String instanceName, String messageType) {
            this.instanceName = instanceName;
            this.messageType = messageType;
        }

        public RepositoryEntry() {
        }

        public String getBaseDir() {
            return baseDir;
        }

        public String getInstanceName() {
            return instanceName;
        }

        public String getMessageType() {
            return messageType;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 59 * hash + Objects.hashCode(this.instanceName);
            hash = 59 * hash + Objects.hashCode(this.messageType);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final RepositoryEntry other = (RepositoryEntry) obj;
            if (!Objects.equals(this.instanceName, other.instanceName)) {
                return false;
            }
            if (!Objects.equals(this.messageType, other.messageType)) {
                return false;
            }
            return true;
        }
    }

    public long getHEAD() {
        try {
            return this.getSvnLogDao().queryRawValue("select MAX(id) from SVNLOG");
        } catch (SQLException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.ERROR, null, ex);
            return 0l;
        }
    }

    public long getCreateTime() {
        return createTime;
    }

    public RepositoryEntry getEntry() {
        return entry;
    }

    public JdbcPooledConnectionSource getConnectionSource() {
        return connectionSource;
    }

}
